home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 October: Mac OS SDK / Dev.CD Oct 97 SDK1.toast / Development Kits (Disc 1) / Apple Game Sprockets / More Sprocket Examples 1.0 / GlyphaIV Sources / G4Play.c < prev    next >
Encoding:
Text File  |  1996-06-12  |  54.1 KB  |  1,710 lines  |  [TEXT/CWIE]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Play.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This (rather large) file handles all player routines while a game is in…
  9. // progress. It gets the player's input, moves the player, tests for collisions…
  10. // and generally handles the "main game loop".  Enemies and actually drawing…
  11. // the graphics to the screen are handled in other files.
  12.  
  13. #include "G4Externs.h"
  14.  
  15. #if GENERATINGPOWERPC
  16.     #include "InputSprocket.h"
  17. #endif
  18.  
  19. #include <ToolUtils.h>
  20. #include <TextUtils.h>
  21. #include <Timer.h>
  22.  
  23. #define kFlapImpulse            48
  24. #define kGlideImpulse            12
  25. #define kAirResistance            2
  26. #define kMaxHVelocity            192
  27. #define kMaxVVelocity            512
  28. #define kNumLightningStrikes    5
  29.  
  30.  
  31. void SetUpLevel (void);
  32. void ResetPlayer (Boolean);
  33. void OffAMortal (void);
  34. void DoCommandKey (void);
  35. void GetPlayerInput (void);
  36. void HandlePlayerIdle (void);
  37. void HandlePlayerFlying (void);
  38. void HandlePlayerWalking (void);
  39. void HandlePlayerSinking (void);
  40. void HandlePlayerFalling (void);
  41. void HandlePlayerBones (void);
  42. void MovePlayer (void);
  43. void CheckTouchDownCollision (void);
  44. void CheckPlatformCollision (void);
  45. void KeepPlayerOnPlatform (void);
  46. void CheckLavaRoofCollision (void);
  47. void SetAndCheckPlayerDest (void);
  48. void HandleLightning (void);
  49. void FinishLightning (void);
  50. void HandleCountDownTimer (void);
  51. void CheckHighScore (void);
  52.  
  53. void HandleLava(void);
  54. void DrawLava(void);
  55.  
  56. playerType    thePlayer;
  57. enemyType    theEnemies[kMaxEnemies];
  58. KeyMap        theKeys;
  59. Rect        platformRects[6], touchDownRects[6], enemyRects[24];
  60. Rect        enemyInitRects[5];
  61. long        theScore, wasTensOfThousands;
  62. short        numLedges, beginOnLevel, levelOn, livesLeft, lightH, lightV;
  63. short        lightningCount, numEnemies, countDownTimer;
  64. Boolean        playing, pausing, flapKeyDown, evenFrame;
  65. Boolean        doEnemyFlapSound, doEnemyScrapeSound;
  66.  
  67. extern    handInfo    theHand;
  68. extern    prefsInfo    thePrefs;
  69. extern    Rect        playerRects[11], mainWindowRect;
  70. extern    short        numOwls;
  71. extern    Boolean        quitting, openTheScores;
  72.  
  73. extern CGrafPtr workSrcMap;
  74.  
  75.  
  76. short oldFrameRate;
  77. short currentFrameRate;
  78. UnsignedWide frameTime;
  79.  
  80. short numScoreFloater = 0;
  81. scoreFloaterType theScoreFloater[kMaxScoreFloater];
  82.  
  83.  
  84.  
  85. //
  86.  
  87. OSErr EasyJoystick(Boolean *left, Boolean *right, Boolean *button);
  88. OSErr EasyJoystick(Boolean *left, Boolean *right, Boolean *button)
  89. {
  90.     // remember a little across calls
  91.     static ISpElementReference lastXAxis = 0;
  92.     static Boolean lastLeft = false;
  93.     static Boolean lastRight = false;
  94.     static Boolean lastButton = false;
  95.  
  96.     int maxEvents = 10;        // process only ten events per round
  97.     
  98.     ISpElementEvent theEvent;            // our event
  99.     Boolean wasEvent = true;
  100.     OSErr err = noErr;
  101.     ISpElementListReference globalList;
  102.     ISpElementInfo info;
  103.     
  104.     *left = lastLeft;
  105.     *right = lastRight;
  106.     *button = lastButton;
  107.  
  108.  
  109.     if (ISpGetGlobalElementList == nil)
  110.     {
  111.         return -1;
  112.     }
  113.  
  114.     err = ISpGetGlobalElementList(&globalList);
  115.  
  116.     while(maxEvents > 0)
  117.     {
  118.         err = ISpElementList_GetNextEvent(globalList, sizeof(theEvent), &theEvent, &wasEvent);
  119.         
  120.         if ((wasEvent == false) || (err))
  121.         {
  122.             break;
  123.         }
  124.         
  125.         ISpElement_GetInfo(theEvent.element, &info);
  126.  
  127.         // get information about this element since we are not doing any configuration
  128.         // we need to know the about labels and kinds
  129.  
  130.         switch(info.theKind)
  131.         {
  132.             case kISpElementKind_Button:
  133.             {
  134.                 Boolean newValue;
  135.                 
  136.                 // figure out the new value for the boolean
  137.                 if (theEvent.data)
  138.                 {
  139.                     newValue = true;
  140.                 }
  141.                 else
  142.                 {
  143.                     newValue = false;
  144.                 }
  145.     
  146.                 *button = newValue;
  147.             }
  148.             break;
  149.             
  150.             case kISpElementKind_DPad:
  151.             {
  152.                 if (info.theLabel == kISpElementLabel_PadMove)
  153.                 {
  154.                     // a movement DPad so they might be using
  155.                     // a console style direction pad so turn
  156.                     // that into axis style data
  157.                     
  158.                     lastXAxis = nil;
  159.                     
  160.                     switch(theEvent.data)
  161.                     {
  162.                         case kISpPadIdle:
  163.                         case kISpPadUp:
  164.                         case kISpPadDown:            
  165.                             *left = false;
  166.                             *right = false;
  167.                             break;
  168.                         case kISpPadLeft:
  169.                         case kISpPadUpLeft:
  170.                         case kISpPadDownLeft:
  171.                             *left = true;
  172.                             *right = false;
  173.                             break;
  174.                         case kISpPadUpRight:
  175.                         case kISpPadRight:
  176.                         case kISpPadDownRight:
  177.                             *left = false;
  178.                             *right = true;
  179.                             break;
  180.                     }
  181.                 }
  182.             }
  183.             break;
  184.             
  185.             case kISpElementKind_Axis:
  186.             {
  187.                 // if it is an axis find out if it is the
  188.                 // x or y axis style data and use that.
  189.                 
  190.                 if (info.theLabel == kISpElementLabel_XAxis)
  191.                 {
  192.                     lastXAxis = theEvent.element;
  193.                 }
  194.                 
  195.                 ISpElement_Flush(theEvent.element);
  196.             }
  197.             break;
  198.         }
  199.         
  200.         maxEvents--;
  201.     }
  202.     
  203.     // poll the last meaningful axis we had on the way out
  204.     // just to make sure we had good data
  205.     if (lastXAxis != nil)
  206.     {
  207.         UInt32 xAxis;
  208.         double calc;
  209.         
  210.         *left = false;
  211.         *right = false;
  212.     
  213.         ISpElement_GetSimpleState(lastXAxis, &xAxis);
  214.  
  215.         calc = xAxis;
  216.         calc -= kISpAxisMiddle;
  217.         calc /= kISpAxisMaximum;
  218.         
  219.         if (calc > 0.2)
  220.         {
  221.             *right = true;
  222.         }
  223.         else if (calc < -0.2)
  224.         {
  225.             *left = true;
  226.         }
  227.     }
  228.     
  229.     return err;
  230. }
  231.  
  232. //==============================================================  Functions
  233. //--------------------------------------------------------------  InitNewGame
  234.  
  235. // This funciton sets up variables and readies for a new game.  It is called…
  236. // only when a the user selects "New Game" - during the course of a game, it…
  237. // is  not called again.
  238.  
  239. void InitNewGame (void)
  240. {                                // Initialize a number of game variables.
  241.     countDownTimer = 0;            // Zero count down timer.
  242.     numLedges = 3;                // Initial number of ledges (platforms).
  243.     beginOnLevel = 1;            // Ledge (platform) the player is on (center ledge).
  244.     levelOn = 0;                // Game level on (first level).
  245.     livesLeft = kInitNumLives;    // Number of player lives remaining.
  246.     theScore = 0L;                // Player's score (a long - can go to 2 billion).
  247.     playing = TRUE;                // Flag playing.
  248.     pausing = FALSE;            // Not paused.
  249.     evenFrame = TRUE;            // Set an initial state for evenFrame.
  250.     wasTensOfThousands = 0L;    // Used for noting when player gets an extra life.
  251.     numOwls = 4;                // Number of "owl" enemies for this level.
  252.     
  253.     numPixelShatter = 0;        // no pixels exploding currently
  254.     numScoreFloater = 0;        // no score floaters
  255.     
  256.     InitHandLocation();            // Get the mummy hand down in the lava.
  257.     theHand.mode = kLurking;    // Flag the hand in "lurking" mode.
  258.     
  259.     ResetPlayer(TRUE);            // Initialize all player variables and put on ledge.
  260.     UpdateLivesNumbers();        // Display number of lives remaining on screen.
  261.     UpdateScoreNumbers();        // Display the player's score (zero at this point).
  262.     UpdateLevelNumbers();        // Display the level (wave) the player is on.
  263.  
  264.     DumpBackToWorkMap();        // Copy background offscreen to "work" offscreen.
  265.  
  266.     SetUpLevel();                // Set up platforms for first level (wave).
  267.     
  268.     GenerateEnemies();            // Prepare all enemies for this level.
  269. }
  270.  
  271. //--------------------------------------------------------------  SetUpLevel
  272.  
  273. // Primarily, this function is called to set up the ledges for the…
  274. // current level (wave) the player is on.  It determines how many…
  275. // are required and then draws these offscreen.  It also flashes…
  276. // the obelisks and strikes the lightning.
  277.  
  278. void SetUpLevel (void)
  279. {
  280.     short        wasLedges, waveMultiple;
  281.     Boolean validLightning[6];
  282.     int invalidLightning;
  283.     
  284.     for(invalidLightning = 0; invalidLightning < 6; invalidLightning++)
  285.     {
  286.         validLightning[invalidLightning] = false;
  287.     }
  288.     
  289.     KillOffEye();                    // Return eye to the aether.
  290.     
  291.     wasLedges = numLedges;            // Remember number of ledges.
  292.     waveMultiple = levelOn % 5;        // Waves repeat every 5th wave (but harder!).
  293.     
  294.     switch (waveMultiple)            // See which of the 5 we're on.
  295.     {
  296.         case 0:                        // Waves 0, 5, 10, …
  297.         numLedges = 5;                // have 5 ledges (platforms) on screen.
  298.         validLightning[5] = true;
  299.         break;
  300.         
  301.         case 1:                        // Waves 1, 6, 11, …
  302.         numLedges = 6;                // are up to 6 ledges (platforms) on screen.
  303.         validLightning[5] = true;
  304.         break;
  305.         
  306.         case 2:                        // Waves 2, 7, 12, …
  307.         numLedges = 5;                // return to 5 ledges (platforms) on screen.
  308.         validLightning[5] = true;
  309.         break;
  310.         
  311.         case 3:                        // Waves 3, 8, 13, …
  312.         numLedges = 3;                // drop to 3 ledges (platforms) on screen.
  313.         validLightning[3] = true;
  314.         validLightning[4] = true;
  315.         break;
  316.         
  317.         case 4:                        // Waves 4, 9, 14, …
  318.         numLedges = 6;                // and return to 6 ledges (platforms) on screen.
  319.         validLightning[3] = true;
  320.         validLightning[4] = true;
  321.         validLightning[5] = true;
  322.         break;
  323.     }
  324.     
  325.     if (wasLedges != numLedges)        // No need to redraw if platforms are unchanged.
  326.         DrawPlatforms(numLedges);
  327.  
  328. #if 0
  329.                             // These are the platforms.  See diagram for numbering.
  330.     SetRect(&platformRects[0], 206, 424, 433, 438);        //_______________
  331.     SetRect(&platformRects[1], -256, 284, 149, 298);    //
  332.     SetRect(&platformRects[2], 490, 284, 896, 298);        //--3--     --4--
  333.     SetRect(&platformRects[3], -256, 105, 149, 119);    //     --5--
  334.     SetRect(&platformRects[4], 490, 105, 896, 119);        //--1--     --2--
  335.     SetRect(&platformRects[5], 233, 190, 407, 204);        //_____--0--_____
  336.  
  337. #endif
  338.  
  339.  
  340.  
  341.     {
  342.         int toast;
  343.         
  344.         if (levelOn == 0)
  345.         {
  346.             for(toast = 0; toast < 6; toast++)
  347.             {
  348.                 validLightning[toast] = true;
  349.             }
  350.         }
  351.         
  352.         for(toast = 0; toast < 6; toast++)
  353.         {
  354.             Rect r = platformRects[toast];
  355.             int diff = (r.right - r.left) / 4;
  356.             int subtoast;
  357.             if (!validLightning[toast])
  358.             {
  359.                 continue;
  360.             }
  361.             
  362.             r.right = r.left + diff;
  363.             
  364.             for(subtoast = 0; subtoast < 4; subtoast++)
  365.             {
  366.                 unsigned long tickWait = TickCount() + 3L;
  367.                 
  368.                 
  369.                 PlayExternalSound(kLightningSound, kLightningPriority);
  370.     
  371.                 DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
  372.                 GetPlayerInput();                        // Get the player's input (keystrokes).
  373.                 MovePlayer();                            // Move the player's bird.
  374.                 
  375.                 HandleLava();
  376.                 DumpBackToWorkMap();                    // clear the screen
  377.                 DrawBanner();
  378.                 DrawLava();
  379.                 DrawPlayer();
  380.  
  381.                 // lightning
  382.                 lightningCount = 1;                        
  383.                 lightH = (r.left + r.right) / 2;
  384.                 lightV = (r.top + r.bottom) / 2;
  385.                 HandleLightning();
  386.  
  387.                 // pixel explosions
  388.                 StartPixelShatterRect(&r, 0, 0, kShatterPlatformLightning);
  389.                 HandlePixelShatter();
  390.                 DrawPixelShatter();
  391.                 
  392.                 DSpContext_SwapBuffers( gTheContext, NULL, NULL );
  393.  
  394.                 r.left += diff;
  395.                 r.right += diff;
  396.  
  397.                 evenFrame = !evenFrame;
  398.                 
  399.                 do                // Wait for 2 Ticks to pass to keep fast Macs at bay.
  400.                 {
  401.                 }
  402.                 while (TickCount() < tickWait);
  403.             }
  404.         }
  405.     }
  406.     
  407.     
  408.     UpdateLevelNumbers();            // Display the current level on screen.
  409. }
  410.  
  411. //--------------------------------------------------------------  ResetPlayer
  412.  
  413. // This function prepares the player - it places the player and his/her mount…
  414. // in their proper starting location (depending on which platform they are to…
  415. // begin on), and it sets all the player's variables to their initial state.
  416.  
  417. void ResetPlayer (Boolean initialPlace)
  418. {
  419.     short        location;
  420.     
  421.     thePlayer.srcNum = 5;            // Set which graphic (frame) the player is to use.
  422.     thePlayer.frame = 320;            // This variable will be used as a coutndown timer.
  423.     
  424.     if (initialPlace)                // If "initialPlace" is TRUE, …
  425.         location = 0;                // the player is to begin on the lowest platform.
  426.     else                            // Otherwise, a random location is chosen.
  427.         location = RandomInt(numLedges);
  428.     
  429.     switch (location)                // Move player horizontally and vertically to their…
  430.     {                                // proper location (based on ledge # they're on).
  431.         case 0:
  432.         thePlayer.h = 296 << 4;        // Bottom center ledge.
  433.         thePlayer.v = 377 << 4;        // We're scaling by 16.
  434.         break;
  435.         
  436.         case 1:
  437.         thePlayer.h = 102 << 4;        // Lower left ledge.
  438.         thePlayer.v = 237 << 4;
  439.         break;
  440.         
  441.         case 2:
  442.         thePlayer.h = 489 << 4;        // Lower right ledge.
  443.         thePlayer.v = 237 << 4;
  444.         break;
  445.         
  446.         case 3:
  447.         thePlayer.h = 102 << 4;        // Top left ledge.
  448.         thePlayer.v = 58 << 4;
  449.         break;
  450.         
  451.         case 4:
  452.         thePlayer.h = 489 << 4;        // Top right ledge.
  453.         thePlayer.v = 58 << 4;
  454.         break;
  455.         
  456.         case 5:
  457.         thePlayer.h = 296 << 4;        // Top central ledge.
  458.         thePlayer.v = 143 << 4;
  459.         break;
  460.     }
  461.                                     // Assign destination rectangle.
  462.     thePlayer.dest = playerRects[thePlayer.srcNum];
  463.     ZeroRectCorner(&thePlayer.dest);
  464.     OffsetRect(&thePlayer.dest, thePlayer.h >> 4, thePlayer.v >> 4);
  465.     thePlayer.wasDest = thePlayer.dest;
  466.     
  467.     thePlayer.hVel = 0;                // Player initially has no velocity.
  468.     thePlayer.vVel = 0;
  469.     thePlayer.facingRight = TRUE;    // We're facing to the right.
  470.     thePlayer.flapping = FALSE;        // We're not flapping our wings initially.
  471.     thePlayer.wrapping = FALSE;        // We can't be wrapping around the edge of the screen.
  472.     thePlayer.clutched = FALSE;        // The hand ain't got us.
  473.     thePlayer.mode = kIdle;            // Our mode is "idle" - waiting to be "born".
  474.     if (lightningCount == 0)        // Prepare for a lightning display to "birth" us.
  475.     {
  476.         lightH = thePlayer.dest.left + 24;
  477.         lightV = thePlayer.dest.bottom - 24;
  478.         lightningCount = kNumLightningStrikes;
  479.     }
  480. }
  481.  
  482. //--------------------------------------------------------------  OffAMortal
  483.  
  484. // Alas, 'tis here that a player is brought who loses a life.
  485.  
  486. void OffAMortal (void)
  487. {
  488.     livesLeft--;                // Decrememnt number of player lives left.
  489.     
  490.     if (livesLeft > 0)            // Indeed, are there lives remaining?
  491.     {
  492.         ResetPlayer(FALSE);        // Good, start a new one off.
  493.         UpdateLivesNumbers();    // Make note of the number of lives remaining.
  494.     }
  495.     else                        // Otherwise, we are at the dreaded "Game Over".
  496.         playing = FALSE;        // Set flag to drop us out of game loop.
  497. }
  498.  
  499. //--------------------------------------------------------------  DoCommandKey
  500.  
  501. // This function handles the case when the user has held down the command…
  502. // key.  Note, this only applies to input when a game is in session - otherwise…
  503. // a standard event loop handles command keys and everything else.
  504.  
  505. void DoCommandKey (void)
  506. {
  507.     if (BitTst(&theKeys, kEKeyMap))            // Test for "command - E"…
  508.     {
  509.         playing = FALSE;                    // which would indicate "End Game".
  510.     }
  511.     else if (BitTst(&theKeys, kPKeyMap))    // Otherwise, see if it's "command - P".
  512.     {
  513.         pausing = TRUE;                        // This means the player is pausing the game.
  514.         MenusReflectMode();                    // Gray-out menus etc.
  515.     }
  516.     else if (BitTst(&theKeys, kQKeyMap))    // Or perhaps the player hit "command - Q".
  517.     {
  518.         playing = FALSE;                    // Set flag to drop out of game loop.
  519.         quitting = TRUE;                    // Set flag to drop out of Glypha.
  520.     }
  521. }
  522.  
  523. //--------------------------------------------------------------  GetPlayerInput
  524.  
  525. // This function looks for keystrokes when a game is underway.  We don't use…
  526. // the more conventional event routines (like GetNextEvent()), because they're…
  527. // notoriously slow, allow background tasks, introduce possible INIT problems,…
  528. // and we don't have to.  Instead, we'll rely on GetKeys() (which has its own…
  529. // set of problems - but we deal with them).
  530.  
  531. void GetPlayerInput (void)
  532. {
  533.     thePlayer.flapping = FALSE;                // Assume we're not flapping.
  534.     thePlayer.walking = FALSE;                // Assume too we're not walking.
  535.  
  536.     GetKeys(theKeys);                        // Get the current keyboard keymap.
  537.     
  538.     // InputSprocket!
  539.     {
  540.         Boolean left = false;
  541.         Boolean right = false;
  542.         Boolean button = false;
  543.         
  544.         EasyJoystick(&left, &right, &button);
  545.         
  546.         if (button)    { BitSet(&theKeys, kDownArrowKeyMap); }
  547.         if (left) { BitSet(&theKeys, kLeftArrowKeyMap); }
  548.         if (right) { BitSet(&theKeys, kRightArrowKeyMap); }
  549.     }
  550.     
  551.     if (BitTst(&theKeys, kCommandKeyMap))    // See first if command key down…
  552.         DoCommandKey();                        // and handle those seperately.
  553.     else                                    // If not command key, continue.
  554.     {                                        // Look for one of the two "flap" keys.
  555.         if ((BitTst(&theKeys, kSpaceBarMap)) || (BitTst(&theKeys, kDownArrowKeyMap)))
  556.         {
  557.             if (thePlayer.mode == kIdle)    // Handle special case when player is idle.
  558.             {
  559.                 thePlayer.mode = kWalking;    // Set the player's mode now to walking.
  560.                 thePlayer.frame = 0;        // Used to note "state" of walking.
  561.             }                                // Otherwise, if player is flying or walking…
  562.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  563.             {
  564.                 if (!flapKeyDown)            // If flap key was not down last frame…
  565.                 {                            // (this is to prevent "automatic fire").
  566.                                             // Give player lift.
  567.                     thePlayer.vVel -= kFlapImpulse;
  568.                     flapKeyDown = TRUE;        // Note that the flap key is down.
  569.                                             // Play the "flap" sound.
  570.                     PlayExternalSound(kFlapSound, kFlapPriority);
  571.                                             // Set player flag to indicate flapping.
  572.                     thePlayer.flapping = TRUE;
  573.                 }
  574.             }
  575.         }
  576.         else
  577.             flapKeyDown = FALSE;            // If flap key not down, remember this.
  578.         
  579.                                             // Test now for one of three "right" keys.
  580.         if ((BitTst(&theKeys, kRightArrowKeyMap) || 
  581.                 BitTst(&theKeys, kSKeyMap) || 
  582.                 BitTst(&theKeys, kQuoteMap)) && 
  583.                 (thePlayer.hVel < kMaxHVelocity))
  584.         {
  585.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  586.             {                                // They are to begin walking (no longer idle).
  587.                 thePlayer.mode = kWalking;
  588.                 thePlayer.frame = 0;
  589.             }
  590.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  591.             {                                // If flying or walking, player moves right.
  592.                 if (!thePlayer.facingRight)    // If facing left, player does an about face.
  593.                 {
  594.                     thePlayer.facingRight = TRUE;
  595.                     if (thePlayer.clutched)
  596.                     {
  597.                         thePlayer.dest.left += 18;
  598.                         thePlayer.dest.right += 18;                        
  599.                         thePlayer.h = thePlayer.dest.left << 4;
  600.                         thePlayer.wasH = thePlayer.h;
  601.                         thePlayer.wasDest = thePlayer.dest;
  602.                     }
  603.                 }                            // Otherwise, if facing right already…
  604.                 else
  605.                 {                            // If flying, add to their horizontal velocity.
  606.                     if (thePlayer.mode == kFlying)
  607.                         thePlayer.hVel += kGlideImpulse;
  608.                     else                    // If walking, set flag to indicate a step.
  609.                         thePlayer.walking = TRUE;
  610.                 }
  611.             }
  612.         }                                    // Test now for one of three "left" keys.
  613.         else if ((BitTst(&theKeys, kLeftArrowKeyMap) || 
  614.                 BitTst(&theKeys, kAKeyMap) || 
  615.                 BitTst(&theKeys, kColonMap)) && 
  616.                 (thePlayer.hVel > -kMaxHVelocity))
  617.         {
  618.             if (thePlayer.mode == kIdle)    // Handle special case when player idle.
  619.             {
  620.                 thePlayer.mode = kWalking;
  621.                 thePlayer.frame = 0;
  622.             }
  623.             else if ((thePlayer.mode == kFlying) || (thePlayer.mode == kWalking))
  624.             {                                // If flying or walking, player moves left.
  625.                 if (thePlayer.facingRight)    // If facing right, player does an about face.
  626.                 {                            // Flag player facing left.
  627.                     thePlayer.facingRight = FALSE;
  628.                     if (thePlayer.clutched)    // Handle case where player gripped by hand.
  629.                     {                        // An about face handled a bit differently.
  630.                         thePlayer.dest.left -= 18;
  631.                         thePlayer.dest.right -= 18;
  632.                         thePlayer.h = thePlayer.dest.left << 4;
  633.                         thePlayer.wasH = thePlayer.h;
  634.                         thePlayer.wasDest = thePlayer.dest;
  635.                     }
  636.                 }
  637.                 else                        // Otherwise, player already facing left.
  638.                 {                            // So player will move left.
  639.                     if (thePlayer.mode == kFlying)
  640.                         thePlayer.hVel -= kGlideImpulse;
  641.                     else
  642.                         thePlayer.walking = TRUE;
  643.                 }
  644.             }
  645.         }
  646.     }
  647. }
  648.  
  649. //--------------------------------------------------------------  HandlePlayerIdle
  650.  
  651. // Following are a number of functions handling the player's different "modes".
  652. // This first function handles the player when in "idle" mode.  When idle, the…
  653. // player is standing on a platform - having just been "born".  This is when the…
  654. // player is in a "safe" mode - meaning no enemy can kill them.  The player remains…
  655. // in idle mode until they hit a key to flap or move or until a timer (thePlayer.frame)…
  656. // counts down to zero.
  657.  
  658. void HandlePlayerIdle (void)
  659. {
  660.     thePlayer.frame--;                // Count down the timer.
  661.     if (thePlayer.frame == 0)        // See if timer has reached zero yet.
  662.         thePlayer.mode = kWalking;    // If so, player is no longer idle.
  663.     
  664.     SetAndCheckPlayerDest();        // Keep player on platform.
  665. }
  666.  
  667. //--------------------------------------------------------------  HandlePlayerFlying
  668.  
  669. // This function handles a player in "flying" mode.  In flying mode, the player…
  670. // is alive and not standing/walking on any platform.  A plyaer remains in flying…
  671. // mode until the player dies (collides unfavorably with an enemy), is caught by…
  672. // the hand, or comes near the top of a platform (in which case they land and…
  673. // switch to walking mode).  While in flying mode, gravity pulls the player down…
  674. // while friction acts to slow the player down.
  675.  
  676. void HandlePlayerFlying (void)
  677. {    
  678.     if (thePlayer.hVel > 0)                    // If player has a positive hori. velocity…
  679.     {                                        // subtract frictional constant from velocity.
  680.         thePlayer.hVel -= kAirResistance;
  681.         if (thePlayer.hVel < 0)                // Don't let it go negative (otherwise, you…
  682.             thePlayer.hVel = 0;                // can get a "yo-yo" effect set up).
  683.     }
  684.     else if (thePlayer.hVel < 0)            // Otherwise, if horizontal velocity negative…
  685.     {                                        // add firctional constant to hori. velocity.
  686.         thePlayer.hVel += kAirResistance;
  687.         if (thePlayer.hVel > 0)
  688.             thePlayer.hVel = 0;
  689.     }
  690.     
  691.     thePlayer.vVel += kGravity;                // Add gravity to player's vertical velocity.
  692.     
  693.     if (thePlayer.vVel > kMaxVVelocity)        // Don't allow player to fall too fast.
  694.         thePlayer.vVel = kMaxVVelocity;
  695.     else if (thePlayer.vVel < -kMaxVVelocity)
  696.         thePlayer.vVel = -kMaxVVelocity;    // And don't allow player to climb too fast.
  697.     
  698.     thePlayer.h += thePlayer.hVel;            // Add velocities to players position.
  699.     thePlayer.v += thePlayer.vVel;
  700.                                             // Now we determine which graphic to use.
  701.     if (thePlayer.facingRight)                // There are the set of right-facing graphics.
  702.     {
  703.         thePlayer.srcNum = 1;                // Assume standard right-facing graphic.
  704.         if (thePlayer.vVel < -kDontFlapVel)    // Now we jump through a series of hoops…
  705.         {                                    // simply to determine whether we'll use…
  706.             if (thePlayer.flapping)            // the graphic of the player with the wings…
  707.                 thePlayer.srcNum = 0;        // up (srcNum = 0) or with the wings down…
  708.             else                            // (srcNum = 1).
  709.                 thePlayer.srcNum = 1;
  710.         }
  711.         else if (thePlayer.vVel > kDontFlapVel)
  712.         {
  713.             if (thePlayer.flapping)
  714.                 thePlayer.srcNum = 1;
  715.             else
  716.                 thePlayer.srcNum = 0;
  717.         }
  718.         else if (thePlayer.flapping)
  719.             thePlayer.srcNum = 0;
  720.     }
  721.     else                                    // If the player is facing left…
  722.     {                                        // We jump through a similar set of hoops…
  723.         thePlayer.srcNum = 2;                // this time choosing between srcNum = 2 …
  724.         if (thePlayer.vVel < -kDontFlapVel)    // and srcNum = 3.
  725.         {
  726.             if (thePlayer.flapping)
  727.                 thePlayer.srcNum = 3;
  728.             else
  729.                 thePlayer.srcNum = 2;
  730.         }
  731.         else if (thePlayer.vVel > kDontFlapVel)
  732.         {
  733.             if (thePlayer.flapping)
  734.                 thePlayer.srcNum = 2;
  735.             else
  736.                 thePlayer.srcNum = 3;
  737.         }
  738.         else if (thePlayer.flapping)
  739.             thePlayer.srcNum = 3;
  740.     }
  741.     
  742.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  743.     
  744.     CheckLavaRoofCollision();                // See if player hit top or bottom of screen.
  745.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  746.     CheckPlatformCollision();                // See if player collided with platform.
  747.     CheckTouchDownCollision();                // See if player has landed on platform.
  748. }
  749.  
  750. //--------------------------------------------------------------  HandlePlayerWalking
  751.  
  752. // This function handles a player in "walking" mode.  They remain in this mode…
  753. // until they walk off a platform's edge, flap to lift off the platform, or…
  754. // collide unfavorably with an enemy (die).  While in walking mode, we need only…
  755. // determine which frame of animation to display (if the player is taking steps)…
  756. // and check for the usual set of collisions.
  757.  
  758. void HandlePlayerWalking (void)
  759. {
  760.     short        desiredHVel;
  761.     
  762.     if (thePlayer.walking)                    // This means user is actively holding down…
  763.     {                                        // the left or right key.
  764.         if (evenFrame)                        // Now we jump through a number of hoops…
  765.         {                                    // in order to get a semi-realistic…
  766.             if (thePlayer.facingRight)        // "stepping" animation going.  We take steps…
  767.             {                                // only on "even frames".
  768.                 if (thePlayer.srcNum == 4)
  769.                     desiredHVel = 208;
  770.                 else
  771.                     desiredHVel = 128;
  772.             }
  773.             else
  774.             {
  775.                 if (thePlayer.srcNum == 7)
  776.                     desiredHVel = -208;
  777.                 else
  778.                     desiredHVel = -128;
  779.             }
  780.             
  781.             if (thePlayer.hVel < desiredHVel)
  782.             {
  783.                 thePlayer.hVel += 80;        // Move player right.
  784.                 if (thePlayer.hVel > desiredHVel)
  785.                 {                            // This is the case where player is walking.
  786.                     thePlayer.hVel = desiredHVel;
  787.                     PlayExternalSound(kWalkSound, kWalkPriority);
  788.                 }
  789.                 else
  790.                 {                        // In this case, player is skidding.
  791.                     PlayExternalSound(kScreechSound, kScreechPriority);
  792.                     StartPixelShatter((thePlayer.dest.left + thePlayer.dest.right) / 2,
  793.                                         thePlayer.dest.bottom, thePlayer.hVel, thePlayer.vVel, kShatterPlatformScrape);
  794.             
  795.                 }
  796.             }
  797.             else
  798.             {
  799.                 thePlayer.hVel -= 80;        // Move player to the left.
  800.                 if (thePlayer.hVel < desiredHVel)
  801.                 {                            // Player is stepping to left.
  802.                     thePlayer.hVel = desiredHVel;
  803.                     PlayExternalSound(kWalkSound, kWalkPriority);
  804.                 }
  805.                 else
  806.                 {                        // Player is skidding to a stop.
  807.                     PlayExternalSound(kScreechSound, kScreechPriority);
  808.                     StartPixelShatter((thePlayer.dest.left + thePlayer.dest.right) / 2,
  809.                                     thePlayer.dest.bottom, thePlayer.hVel, thePlayer.vVel, kShatterPlatformScrape);
  810.  
  811.                 }
  812.             }
  813.         }
  814.     }
  815.     else                                    // If user is not actively holding down the…
  816.     {                                        // left or right key, bring player to a stop.
  817.         thePlayer.hVel -= thePlayer.hVel / 4;
  818.         if ((thePlayer.hVel < 4) && (thePlayer.hVel > -4))
  819.             thePlayer.hVel = 0;                // If close to zero (within 4), stop player.
  820.         else                                // Othewrwise, play the skidding sound.
  821.             PlayExternalSound(kScreechSound, kScreechPriority);
  822.     }
  823.     
  824.     if (thePlayer.vVel > kMaxVVelocity)        // Keep player from moving too quickly…
  825.         thePlayer.vVel = kMaxVVelocity;        // left or right.
  826.     else if (thePlayer.vVel < -kMaxVVelocity)
  827.         thePlayer.vVel = -kMaxVVelocity;
  828.     
  829.     thePlayer.h += thePlayer.hVel;            // Move player horizontally and vertically…
  830.     thePlayer.v += thePlayer.vVel;            // by the corresponding velocity.
  831.     
  832.     if (thePlayer.walking)                    // "If player holding down left or right keys…".
  833.     {
  834.         if (evenFrame)                        // Here's where we toggle between the two…
  835.         {                                    // frames of "stepping" animation.
  836.             if (thePlayer.facingRight)
  837.                 thePlayer.srcNum = 9 - thePlayer.srcNum;
  838.             else
  839.                 thePlayer.srcNum = 13 - thePlayer.srcNum;
  840.         }
  841.     }
  842.     else                                    // If the player not holding down keys…
  843.     {                                        // draw the player just standing there.
  844.         if (thePlayer.facingRight)
  845.             thePlayer.srcNum = 5;
  846.         else
  847.             thePlayer.srcNum = 6;
  848.     }
  849.     
  850.     SetAndCheckPlayerDest();                // Check for wrap-around and all that.
  851.     
  852.     CheckTouchDownCollision();                // See if player still on platform.
  853.     KeepPlayerOnPlatform();                    // Don't let player "sink through" ledge.
  854.     CheckPlayerEnemyCollision();            // See if player hit an enemy.
  855. }
  856.  
  857. //--------------------------------------------------------------  HandlePlayerSinking
  858.  
  859. // When the player is in "sinking" mode, they are on a one-way ticket to death.
  860. // The player is sinking into the lava.  We put the player into this mode (rather…
  861. // than kill them outright) so that we can have a number of frames of them slowly…
  862. // slipping beneath the surface of the lava.  When the get below the surface of…
  863. // the lava, they will be officially "killed" and a new player will be "born",
  864.  
  865. void HandlePlayerSinking (void)
  866. {
  867.     thePlayer.hVel = 0;                        // Don't allow horizontal motion.
  868.     thePlayer.vVel = 16;                    // They will sink at this constant rate.
  869.     if (thePlayer.dest.top > kLavaHeight)    // See if they slipped below the surface.
  870.         OffAMortal();                        // If they did, kill 'em.
  871.     
  872.     thePlayer.v += thePlayer.vVel;            // Otherwise, move them down a notch.
  873.     
  874.     SetAndCheckPlayerDest();                // Check for wrap-around, etc.
  875. }
  876.  
  877. //--------------------------------------------------------------  HandlePlayerFalling
  878.  
  879. // "Falling" refers to a player who is dead already but is still careening…
  880. // down the screen as a skeleton.  If (when) the player lands on a ledge they…
  881. // will turn into a pile of bones for a short duration.  If instead they fall…
  882. // into the lava, they'll sink.  In any event, it is then that they are…
  883. // officially pronounced dead and a new player is born.
  884.  
  885. void HandlePlayerFalling (void)
  886. {
  887.     if (thePlayer.hVel > 0)                // Handle horizontal air resistance.
  888.     {
  889.         thePlayer.hVel -= kAirResistance;
  890.         if (thePlayer.hVel < 0)
  891.             thePlayer.hVel = 0;
  892.     }
  893.     else if (thePlayer.hVel < 0)
  894.     {
  895.         thePlayer.hVel += kAirResistance;
  896.         if (thePlayer.hVel > 0)
  897.             thePlayer.hVel = 0;
  898.     }
  899.     
  900.     thePlayer.vVel += kGravity;            // Add in effect of gravity.
  901.     
  902.     if (thePlayer.vVel > kMaxVVelocity)    // Keep player from falling too fast.
  903.         thePlayer.vVel = kMaxVVelocity;
  904.     else if (thePlayer.vVel < -kMaxVVelocity)
  905.         thePlayer.vVel = -kMaxVVelocity;
  906.     
  907.     thePlayer.h += thePlayer.hVel;        // Move player's x and y (h and v)…
  908.     thePlayer.v += thePlayer.vVel;        // by amount of velocity in each direction.
  909.     
  910.     SetAndCheckPlayerDest();            // Check for wrap-around, etc.
  911.     
  912.     CheckLavaRoofCollision();            // See if they hit roof or lava.
  913.     CheckPlatformCollision();            // See if they crashed to a ledge.
  914. }
  915.  
  916. //--------------------------------------------------------------  HandlePlayerBones
  917.  
  918. // This is when the player is just a static pile of bones on a platform.  They…
  919. // have been killed by an enemy and now are waiting to slip away so that a new…
  920. // player can be born.
  921.  
  922. void HandlePlayerBones (void)
  923. {
  924.     if (evenFrame)                    // To slow it down a bit, action only occurs…
  925.     {                                // on the even frames.
  926.         thePlayer.frame--;            // Drop the counter down by one.
  927.         if (thePlayer.frame == 0)    // When counter reaches zero, player officially dies.
  928.             OffAMortal();
  929.         else                        // Otherwise, player's bones are sinking.
  930.             thePlayer.dest.top = thePlayer.dest.bottom - thePlayer.frame;
  931.     }
  932. }
  933.  
  934. //--------------------------------------------------------------  MovePlayer
  935.  
  936. // This function is the sort of "master movement" function.  It looks…
  937. // at what mode a player is in and calls the appropriate function from…
  938. // above.  Arcade games (at least this one) tend to be very "modal" in…
  939. // this way.  It's the actions of the user and the enemies in the game…
  940. // that cause the player's mode to move from one state to another.
  941.  
  942. void MovePlayer (void)
  943. {
  944.     switch (thePlayer.mode)        // Check the "mode" the player is in.
  945.     {
  946.         case kIdle:                // Invulnerable - standing there - just born.
  947.         HandlePlayerIdle();
  948.         break;
  949.         
  950.         case kFlying:            // Flapping, floating, airborne.
  951.         HandlePlayerFlying();
  952.         break;
  953.         
  954.         case kWalking:            // On terra firma.  Standing or walking on ledge.
  955.         HandlePlayerWalking();
  956.         break;
  957.         
  958.         case kSinking:            // Trapped in the lava - going down.
  959.         HandlePlayerSinking();
  960.         break;
  961.         
  962.         case kFalling:            // Dead - a skeleton falling to earth.
  963.         HandlePlayerFalling();
  964.         break;
  965.         
  966.         case kBones:            // Dead - a static pile of bones on a ledge.
  967.         HandlePlayerBones();
  968.         break;
  969.     }
  970. }
  971.  
  972. //--------------------------------------------------------------  CheckTouchDownCollision
  973.  
  974. // This function determines whether or not the player is landed on a ledge.
  975. // It does this by doing a rectangle collision between the player's bounding…
  976. // rectangle and an imaginary rectangle enclosing an area above the ledges.
  977. // I call these imaginary rectangles "touchDownRects[]".  The trick was that…
  978. // you don't want the player to have to "hit" the top of a ledge in order to…
  979. // land on it - there is an arbitrary distance above a ledge where, if the player…
  980. // is within this area, the legs ought to come out and the player flagged as…
  981. // walking.  As well, this same function is used for a walking player to see…
  982. // if they are still on the ledge (they may walk off the edge).
  983.  
  984. void CheckTouchDownCollision (void)
  985. {
  986.     Rect        testRect, whoCares;
  987.     short        i, offset;
  988.     Boolean        sected;
  989.     
  990.     sected = FALSE;                                // Assume not on ledge.
  991.     for (i = 0; i < numLedges; i++)                // Go through all ledges.
  992.     {
  993.         testRect = touchDownRects[i];            // Here's the imaginary rect.
  994.         if (thePlayer.mode == kWalking)            // We need an offset if player walking…
  995.             OffsetRect(&testRect, 0, 11);        // since the player graphic is taller.
  996.         
  997.         if (SectRect(&thePlayer.dest, &testRect, &whoCares))
  998.         {                                        // Does the player's rect intersect?
  999.             if (thePlayer.mode == kFlying)        // Okay, it does, is the player airborne?
  1000.             {
  1001.                 StartPixelShatter((thePlayer.dest.right+thePlayer.dest.left)/2,
  1002.                                     thePlayer.dest.bottom,
  1003.                                     thePlayer.hVel,
  1004.                                     thePlayer.vVel, kShatterPlatformScrape);
  1005.  
  1006.                 thePlayer.mode = kWalking;        // Put player into walking mode.
  1007.                 if (thePlayer.facingRight)        // Assign correct graphic for player.
  1008.                     thePlayer.srcNum = 5;
  1009.                 else
  1010.                     thePlayer.srcNum = 6;
  1011.                 if (thePlayer.vVel > 0)            // Stop player from falling further.
  1012.                     thePlayer.vVel = 0;
  1013.                 thePlayer.dest.bottom += 11;    // "Grow" player's bounding rect.
  1014.                 thePlayer.wasDest.bottom += 11;
  1015.                                                 // Move player so standing on top of ledge.
  1016.                 offset = thePlayer.dest.bottom - testRect.bottom - 1;
  1017.                 thePlayer.dest.bottom -= offset;
  1018.                 thePlayer.dest.top -= offset;
  1019.                 thePlayer.v = thePlayer.dest.top << 4;
  1020.                                                 // Play brief collision sound.
  1021.                 PlayExternalSound(kGrateSound, kGratePriority);
  1022.             }
  1023.             
  1024.             sected = TRUE;                        // Make note that we've landed.
  1025.         }
  1026.     }
  1027.     
  1028.     if (!sected)                                // Now, if we didn't collide…
  1029.     {                                            // were we walking?
  1030.         if (thePlayer.mode == kWalking)            // Did we walk off the ledge?
  1031.         {
  1032.             thePlayer.mode = kFlying;            // Set player to flying mode.
  1033.             thePlayer.dest.bottom -= 11;        // Resize player's bounding rect.
  1034.             thePlayer.wasDest.bottom -= 11;
  1035.         }
  1036.     }
  1037. }
  1038.  
  1039. //--------------------------------------------------------------  CheckPlatformCollision
  1040.  
  1041. // Unlike the above function, this one tests the player's bounding rect against…
  1042. // the bounding rect of each ledge (not an imaginary rect above the ledge).  This…
  1043. // function is primarily for (then) collisions off the bottom and sides of the…
  1044. // ledges.  In this way, the ledges are "solid" - not able to be passed through.
  1045.  
  1046. void CheckPlatformCollision (void)
  1047. {
  1048.     Rect        hRect, vRect, whoCares;
  1049.     short        i, offset;
  1050.     
  1051.     for (i = 0; i < numLedges; i++)                    // Walk through all ledges.
  1052.     {                                                // Test rectangle overlap.
  1053.         if (SectRect(&thePlayer.dest, &platformRects[i], &whoCares))
  1054.         {                                            // If player intersecting ledge…
  1055.             hRect.left = thePlayer.dest.left;        // Create our special test rect.
  1056.             hRect.right = thePlayer.dest.right;
  1057.             hRect.top = thePlayer.wasDest.top;
  1058.             hRect.bottom = thePlayer.wasDest.bottom;
  1059.                                                     // Determine if the player hit the…
  1060.                                                     // top/bottom of the ledge or the…
  1061.                                                     // sides of the ledge.
  1062.             if (SectRect(&hRect, &platformRects[i], &whoCares))
  1063.             {                                        // We're fairly sure the player hit…
  1064.                                                     // the left or right edge of ledge.
  1065.                                                     
  1066.                 PlayExternalSound(kGrateSound, kGratePriority);
  1067.                 
  1068.                 StartPixelShatter(    (thePlayer.dest.right+thePlayer.dest.left)/2,
  1069.                                     thePlayer.dest.bottom,
  1070.                                     thePlayer.hVel,
  1071.                                     thePlayer.vVel, kShatterPlatformHit);
  1072.                                     
  1073.                 if (thePlayer.h > thePlayer.wasH)    // If player was heading right…
  1074.                 {                                    // player will bounce to left.
  1075.                     offset = thePlayer.dest.right - platformRects[i].left;
  1076.                     thePlayer.dest.left -= offset;
  1077.                     thePlayer.dest.right -= offset;
  1078.                     thePlayer.h = thePlayer.dest.left << 4;
  1079.                     if (thePlayer.hVel > 0)            // We bounce back with 1/2 our vel.
  1080.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  1081.                     else
  1082.                         thePlayer.hVel = thePlayer.hVel >> 1;
  1083.                 }                                    // Else if player was heading left…
  1084.                 else if (thePlayer.h < thePlayer.wasH)
  1085.                 {                                    // player will bounce right.
  1086.                     offset = platformRects[i].right - thePlayer.dest.left;
  1087.                     thePlayer.dest.left += offset;
  1088.                     thePlayer.dest.right += offset;
  1089.                     thePlayer.h = thePlayer.dest.left << 4;
  1090.                     if (thePlayer.hVel < 0)            // We bounce back with 1/2 our vel.
  1091.                         thePlayer.hVel = -(thePlayer.hVel >> 1);
  1092.                     else
  1093.                         thePlayer.hVel = thePlayer.hVel >> 1;
  1094.                 }                                    // Play impact sound.
  1095.  
  1096.             }
  1097.             else                                    // It doesn't look like we hit the…
  1098.             {                                        // the left or right edge of ledge.
  1099.                 vRect.left = thePlayer.wasDest.left;
  1100.                 vRect.right = thePlayer.wasDest.right;
  1101.                 vRect.top = thePlayer.dest.top;
  1102.                 vRect.bottom = thePlayer.dest.bottom;
  1103.                                                     // So we'll test top/bottom collision.
  1104.                 if (SectRect(&vRect, &platformRects[i], &whoCares))
  1105.                 {                                    // We've decided we've hit top/bottom.
  1106.                     if (thePlayer.wasV < thePlayer.v)
  1107.                     {                                // If we were heading down (hit top)…
  1108.                                                     // keep player on top of ledge.
  1109.                         offset = thePlayer.dest.bottom - platformRects[i].top;
  1110.                         thePlayer.dest.top -= offset;
  1111.                         thePlayer.dest.bottom -= offset;
  1112.                         thePlayer.v = thePlayer.dest.top << 4;
  1113.                                                     // Play collision sound.
  1114.                         if (thePlayer.vVel > kDontFlapVel)
  1115.                         {                
  1116.                             StartPixelShatter(    (thePlayer.dest.right+thePlayer.dest.left)/2,
  1117.                                     thePlayer.dest.bottom,
  1118.                                     thePlayer.hVel,
  1119.                                     thePlayer.vVel, kShatterPlatformScrape);
  1120.  
  1121.                             PlayExternalSound(kGrateSound, kGratePriority);
  1122.                         }
  1123.                                                     // If we were falling bones (dead)…
  1124.                         if (thePlayer.mode == kFalling)
  1125.                         {                            // we'll bounce.
  1126.                             if ((thePlayer.dest.right - 16) > platformRects[i].right)                            {
  1127.                                 thePlayer.hVel = 16;
  1128.                                 if (thePlayer.vVel > 0)
  1129.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  1130.                                 else
  1131.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  1132.                             }
  1133.                             else if ((thePlayer.dest.left + 16) < platformRects[i].left)
  1134.                             {
  1135.                                 thePlayer.hVel = -16;
  1136.                                 if (thePlayer.vVel > 0)
  1137.                                     thePlayer.vVel = -(thePlayer.vVel >> 1);
  1138.                                 else
  1139.                                     thePlayer.vVel = thePlayer.vVel >> 1;
  1140.                             }
  1141.                             else                    // If we were nearly stopped…
  1142.                             {                        // turn into pile of bones.
  1143.                                 PlayExternalSound(kBoom1Sound, kBoom1Priority);
  1144.                                 thePlayer.vVel = 0;
  1145.                                 thePlayer.mode = kBones;
  1146.                                 thePlayer.frame = 22;
  1147.                                 thePlayer.dest.top = thePlayer.dest.bottom - 22;
  1148.                                 thePlayer.v = thePlayer.dest.top << 4;
  1149.                                 thePlayer.srcNum = 10;
  1150.                             }
  1151.                         }
  1152.                         else                        // Okay, if we weren't falling bones…
  1153.                         {                            // bounce the player (-1/2 vel.).
  1154.                             if (thePlayer.vVel > 0)
  1155.                                 thePlayer.vVel = -(thePlayer.vVel >> 1);
  1156.                             else
  1157.                                 thePlayer.vVel = thePlayer.vVel >> 1;
  1158.                         }
  1159.                     }                                // If the player was instead moving up…
  1160.                     else if (thePlayer.wasV > thePlayer.v)
  1161.                     {                                // the player likely hit the bottom of…
  1162.                                                     // the ledge.  Keep player below ledge.
  1163.                                                     
  1164.                         StartPixelShatter(    (thePlayer.dest.right+thePlayer.dest.left)/2,
  1165.                                             thePlayer.dest.bottom,
  1166.                                             thePlayer.hVel,
  1167.                                             thePlayer.vVel, kShatterPlatformHit);
  1168.                         
  1169.                         offset = platformRects[i].bottom - thePlayer.dest.top;
  1170.                         thePlayer.dest.top += offset;
  1171.                         thePlayer.dest.bottom += offset;
  1172.                         thePlayer.v = thePlayer.dest.top << 4;
  1173.                                                     // Play collision sound.
  1174.                         PlayExternalSound(kGrateSound, kGratePriority);
  1175.                         if (thePlayer.vVel < 0)        // Bounce player down (-1/2 vel.).
  1176.                             thePlayer.vVel = -(thePlayer.vVel >> 1);
  1177.                         else
  1178.                             thePlayer.vVel = thePlayer.vVel >> 1;
  1179.                     }
  1180.                 }
  1181.             }
  1182.         }
  1183.     }
  1184. }
  1185.  
  1186. //--------------------------------------------------------------  KeepPlayerOnPlatform
  1187.  
  1188. // This is an alignment function.  It is called only if the player is standing or…
  1189. // walking on a ledge.  It is designed to keep the player's mount's (bird's)…
  1190. // feet firmly planted on the ledge.  Consider that, with the addition of gravity…
  1191. // to a player's downward velocity, there is a problem where the player can appear…
  1192. // to slowly sink down through the ledge.  There may be any number of methods you…
  1193. // might want to try to prevent this from becoming a problem in the first place, …
  1194. // but my experience has been that all the methods I've tried have flaws - correcting…
  1195. // for those flaws points out other flaws and you start getting a messy sort of…
  1196. // patchwork.  Should you ever get it to work, the mess that is your function has come…
  1197. // to resemble the Knot of ????.
  1198.  
  1199. void KeepPlayerOnPlatform (void)
  1200. {
  1201.     Rect        whoCares;
  1202.     short        i, offset;
  1203.     
  1204.     for (i = 0; i < numLedges; i++)        // For each ledge for this wave…
  1205.     {                                    // test for a collision.
  1206.         if ((SectRect(&thePlayer.dest, &platformRects[i], &whoCares)) && (thePlayer.vVel > 0))
  1207.         {                                // If collided (player sinking), force…
  1208.                                         // player to top of ledge.
  1209.             offset = thePlayer.dest.bottom - platformRects[i].top - 1;
  1210.             thePlayer.dest.top -= offset;
  1211.             thePlayer.dest.bottom -= offset;
  1212.             thePlayer.v = thePlayer.dest.top * 16;
  1213.         }
  1214.     }
  1215.     
  1216.     if (thePlayer.vVel > 0)                // Set player's vertical velocity to zero.
  1217.         thePlayer.vVel = 0;
  1218. }
  1219.  
  1220. //--------------------------------------------------------------  CheckLavaRoofCollision
  1221.  
  1222. // This is a simple high/low test to see if the player has either bounced off…
  1223. // the roof of the "arena" or dipped down into the lava below.
  1224.  
  1225. void CheckLavaRoofCollision (void)
  1226. {
  1227.     short        offset;
  1228.     
  1229.     if (thePlayer.dest.bottom > kLavaHeight)    // See if player in lava.
  1230.     {
  1231.         if (thePlayer.mode == kFalling)            // If falling (dead), "Splash!"
  1232.             PlayExternalSound(kSplashSound, kSplashPriority);
  1233.         else                                    // If flying (alive), "Yeow!"
  1234.             PlayExternalSound(kBirdSound, kBirdPriority);
  1235.     
  1236.         {
  1237.             short left = thePlayer.dest.left;
  1238.             short right = thePlayer.dest.right;
  1239.             short bottom = thePlayer.dest.bottom;
  1240.             short delta = (right - left);
  1241.             short temp = left * 32;
  1242.             short splashItr;
  1243.             
  1244.             for(splashItr = 0; splashItr < 32; splashItr++)
  1245.             {
  1246.                 StartPixelShatter(temp / 32, bottom, (thePlayer.hVel * 0.75), -(thePlayer.vVel * 0.75), kShatterLavaSplash);
  1247.                 temp += delta;
  1248.             }
  1249.         }
  1250.  
  1251.         thePlayer.mode = kSinking;                // Irregardless, player is now sinking.
  1252.     }
  1253.     else if (thePlayer.dest.top < kRoofHeight)    // See if player hit roof.
  1254.     {                                            // Move player to below roof.
  1255.         offset = kRoofHeight - thePlayer.dest.top;
  1256.         thePlayer.dest.top += offset;
  1257.         thePlayer.dest.bottom += offset;
  1258.         thePlayer.v = thePlayer.dest.top * 16;
  1259.                                                 // Play collision sound.
  1260.         PlayExternalSound(kGrateSound, kGratePriority);
  1261.         thePlayer.vVel = thePlayer.vVel / -2;    // Rebound player (-1/2 vel.).
  1262.     }
  1263. }
  1264.  
  1265. //--------------------------------------------------------------  SetAndCheckPlayerDest
  1266.  
  1267. // This function keeps our player's screen coordinates and "scaled" coordinates…
  1268. // in agreement.  As well, it checks for wrap-around and handles it.
  1269.  
  1270. void SetAndCheckPlayerDest (void)
  1271. {
  1272.     short        wasTall, wasWide;
  1273.                                         // Remember width and height of player.
  1274.     wasTall = thePlayer.dest.bottom - thePlayer.dest.top;
  1275.     wasWide = thePlayer.dest.right - thePlayer.dest.left;
  1276.                                         // Convert scaled coords to screen coords.
  1277.     thePlayer.dest.left = thePlayer.h >> 4;
  1278.     thePlayer.dest.right = thePlayer.dest.left + wasWide;
  1279.     thePlayer.dest.top = thePlayer.v >> 4;
  1280.     thePlayer.dest.bottom = thePlayer.dest.top + wasTall;
  1281.     
  1282.     if (thePlayer.dest.left > 640)        // Has player left right side of arena?
  1283.     {                                    // Wrap player back to left side of screen.
  1284.         OffsetRect(&thePlayer.dest, -640, 0);
  1285.         thePlayer.h = thePlayer.dest.left << 4;
  1286.         OffsetRect(&thePlayer.wasDest, -640, 0);
  1287.     }
  1288.     else if (thePlayer.dest.right < 0)    // Else, has player left left side of screen?
  1289.     {                                    // Wrap player around to right side of screen.
  1290.         OffsetRect(&thePlayer.dest, 640, 0);
  1291.         thePlayer.h = thePlayer.dest.left << 4;
  1292.         OffsetRect(&thePlayer.wasDest, 640, 0);
  1293.     }
  1294. }
  1295.  
  1296. //--------------------------------------------------------------  HandleLightning
  1297.  
  1298. // Lightning is handled here.  Obelisks are flashed, lightning is generated, …
  1299. // lighting strikes, and the lightning counter decremented.  This is pretty…
  1300. // nice - we can just set "lightningCount" to a non-zero number and this…
  1301. // function will strike lightning every fram until the counter returns to zero.
  1302.  
  1303. void HandleLightning (void)
  1304. {
  1305.     Boolean doLightning = false;
  1306.     short hLoc;
  1307.     short vLoc;
  1308.     
  1309.     if (thePlayer.electrical > 0)
  1310.     {
  1311.         lightningCount = 0;
  1312.         
  1313.         hLoc = (thePlayer.dest.left + thePlayer.dest.right) / 2;
  1314.         vLoc = (thePlayer.dest.top + thePlayer.dest.bottom) / 2;
  1315.         thePlayer.electrical--;
  1316.         
  1317.         doLightning = true;
  1318.     }
  1319.     else if (lightningCount > 0)                        // Is lightning to strik this frame?
  1320.     {    
  1321.         lightningCount--;
  1322.         hLoc = lightH;
  1323.         vLoc = lightV;
  1324.         
  1325.         doLightning = true;
  1326.     }
  1327.     
  1328.     if (doLightning)
  1329.     {
  1330.         GenerateLightning(hLoc, vLoc);
  1331.         StrikeLightningWork();
  1332.     }
  1333. }
  1334.  
  1335. //--------------------------------------------------------------  HandleCountDownTimer
  1336.  
  1337. // This is a pretty boring function.  It is here so that when one level ends,…
  1338. // the next one does begin immediately.  It gives the player a few seconds of…
  1339. // breathing time.  Essentially, to engage it, we need merely set "countDownTimer"…
  1340. // to a positive number.  Each frame the counter gets decremented.  When it…
  1341. // reaches zero, the level is advanced to the next wave.
  1342.  
  1343. void HandleCountDownTimer (void)
  1344. {
  1345.     if (countDownTimer == 0)        // If already zero, do nothing.
  1346.         return;
  1347.     else                            // Otherwise, if greater than zero…
  1348.     {
  1349.         countDownTimer--;            // decrememnt counter.
  1350.         if (countDownTimer == 0)    // Did it just hit zero?
  1351.         {
  1352.             countDownTimer = 0;        // Well, just to be sure (dumb line of code).
  1353.             levelOn++;                // Increment the level (wave) we're on.
  1354.             UpdateLevelNumbers();    // Display new level on screen.
  1355.             SetUpLevel();            // Set up the platforms.
  1356.             GenerateEnemies();        // Ready nemesis.
  1357.         }
  1358.     }
  1359. }
  1360.  
  1361. //--------------------------------------------------------------  PlayGame
  1362.  
  1363. // Here is the "core" of the "game loop".  When a player has elected to…
  1364. // begin a game, Glypha falls into this function and remains in a loop…
  1365. // herein until the player either quits, or loses their last "bird".
  1366. // Each pass through the main loop below constitutes one "frame" of the game.
  1367.  
  1368. void PlayGame (void)
  1369. {
  1370.     Point        offsetPt;
  1371.     UnsignedWide thisTime;
  1372.     
  1373.     offsetPt.h = 0;                                // Set up ShieldCursor() point.
  1374.     offsetPt.v = 20;
  1375.  
  1376.  
  1377.     oldFrameRate = 0;
  1378.     Microseconds(&frameTime);
  1379.     
  1380.     frameTime.lo += 1000000;
  1381.         
  1382.     do                                            // Main game loop!!!!
  1383.     {
  1384.         Microseconds(&thisTime);
  1385.         
  1386.         if ((thisTime.hi > frameTime.hi) || 
  1387.             ((thisTime.hi == frameTime.hi) && (thisTime.lo > frameTime.lo)))
  1388.         {
  1389.             UpdateLevelNumbers();                    // update the level, using for frame rate
  1390.             oldFrameRate = currentFrameRate;
  1391.             currentFrameRate = 0;
  1392.             Microseconds(&frameTime);
  1393.             frameTime.lo += 1000000;
  1394.         }
  1395.         else
  1396.         {
  1397.             currentFrameRate++;
  1398.         }
  1399.         
  1400.         MovePlayer();                            // Move the player's bird.
  1401.         MoveEnemies();                            // Move all sphinx enemies.
  1402.         HandleHand();                            // Handle the mummy hand (may do nothing).
  1403.         HandleEye();                            // Handle eye (probably will do nothing).
  1404.         HandlePixelShatter();
  1405.         HandleScoreFloaters();
  1406.         
  1407.         HandleLava();
  1408.         
  1409.         DSpContext_GetBackBuffer( gTheContext, kDSpBufferKind_Normal, &workSrcMap );
  1410.  
  1411.         
  1412.         DumpBackToWorkMap();                    // clear the screen
  1413.         DrawFrame();                            // Draw the whole scene for this frame.
  1414.         HandleLightning();
  1415.  
  1416.         DSpContext_SwapBuffers( gTheContext, NULL, NULL );
  1417.  
  1418.         evenFrame = !evenFrame;                    // Toggle "evenFrame" variable.
  1419.         
  1420.         GetPlayerInput();                        // Get the player's input (keystrokes).
  1421.         HandleCountDownTimer();                    // Handle countdown (may do nothing).
  1422.     }
  1423.     while ((playing) && (!pausing));            // Stay in loop until dead, paused or quit.
  1424.     
  1425.     if ((!playing) && (!quitting))                // If the player died!
  1426.     {                                            // Then play some sweet music.
  1427.         PlayExternalSound(kMusicSound, kMusicPriority);
  1428.         CheckHighScore();                        // And see if they're on the high scores.
  1429.     }
  1430.     
  1431.     MenusReflectMode();                            // Set the menus grayed-out state correctly.
  1432.     FlushEvents(everyEvent, 0);                    // Flush any events in the queue.
  1433. }
  1434.  
  1435. //--------------------------------------------------------------  CheckHighScore
  1436.  
  1437. // This function handles testing to see if the player's score is in the …
  1438. // high scores.  If that is the case, the function prompts the user for…
  1439. // a name to enter, and sorts and stores off the new score list.
  1440.  
  1441. void CheckHighScore (void)
  1442. {
  1443.     #define        kHighNameDialogID    130
  1444.     Str255        placeStr, tempStr;
  1445.     DialogPtr    theDial;
  1446.     short        i, item;
  1447.     Boolean        leaving;
  1448.     
  1449.     if (theScore > thePrefs.highScores[9])        // To see if on high scores, we need…
  1450.     {                                            // merely see if the last guy is beat out.
  1451.         openTheScores = TRUE;                    // Will automatically bring up high scores.
  1452.                                                 // Play some congratulatory music.
  1453.         PlayExternalSound(kBonusSound, kMusicPriority - 1);
  1454.         i = 8;                                    // Find where new score fits in list.
  1455.         while ((theScore > thePrefs.highScores[i]) && (i >= 0))
  1456.         {                                        // We'll bump everyone down as we look.
  1457.             thePrefs.highScores[i + 1] = thePrefs.highScores[i];
  1458.             thePrefs.highLevel[i + 1] = thePrefs.highLevel[i];
  1459.             PasStringCopy(thePrefs.highNames[i], thePrefs.highNames[i + 1]);
  1460.             i--;
  1461.         }
  1462.         
  1463.         i++;                                    // i is our place in list (zero based).
  1464.         thePrefs.highScores[i] = theScore;        // Pop the new score in place.
  1465.         thePrefs.highLevel[i] = levelOn + 1;    // Drop in the new highest level.
  1466.         
  1467.         NumToString((long)i + 1L, placeStr);    // Convert place to a string to display…
  1468.         ParamText(placeStr, "\p", "\p", "\p");    // in the dialog (via ParamText()).
  1469.         
  1470.         InitCursor();                            // Show cursor.
  1471.         CenterDialog(kHighNameDialogID);        // Center the dialog and then bring it up.
  1472.         theDial = GetNewDialog(kHighNameDialogID, 0L, kPutInFront);
  1473.         SetPort((GrafPtr)theDial);
  1474.         ShowWindow((GrafPtr)theDial);            // Make dialog visible.
  1475.         DrawDefaultButton(theDial);                // Draw outline around "Okay" button.
  1476.         FlushEvents(everyEvent, 0);                // Flush any events queued up.
  1477.                                                 // Put a default name in text edit box.
  1478.         SetDialogString(theDial, 2, thePrefs.highName);
  1479.         SelIText(theDial, 2, 0, 1024);            // Select the whole text edit string.
  1480.         leaving = FALSE;                        // Flag for noting when player hit "Okay".
  1481.         
  1482.         while (!leaving)                        // Simple modal dialog loop.
  1483.         {
  1484.             ModalDialog(0L, &item);                // Use standard filtering.
  1485.             
  1486.             if (item == 1)                        // If player hit the "Okay" button…
  1487.             {                                    // Get the name entered in text edit box.
  1488.                 GetDialogString(theDial, 2, tempStr);
  1489.                                                 // Copy the name into high score list.
  1490.                 PasStringCopyNum(tempStr, thePrefs.highNames[i], 15);
  1491.                 PasStringCopy(thePrefs.highNames[i], thePrefs.highName);
  1492.                 leaving = TRUE;                    // We're gone!
  1493.             }
  1494.         }
  1495.         
  1496.         DisposDialog(theDial);                    // Clean up.
  1497.     }
  1498.     else                                        // But if player didn't get on high scores…
  1499.         openTheScores = FALSE;                    // no need to rub their face in it.
  1500. }
  1501.  
  1502.  
  1503. void HandlePixelShatter(void)
  1504. {
  1505.     int i = 0;
  1506.     
  1507.     while(i < numPixelShatter)
  1508.     {
  1509.         if (thePixelShatter[i].duration == 0)
  1510.         {
  1511.             thePixelShatter[i] = thePixelShatter[numPixelShatter];
  1512.             numPixelShatter--;
  1513.             continue;
  1514.         }
  1515.         
  1516.         thePixelShatter[i].duration--;
  1517.         thePixelShatter[i].h += thePixelShatter[i].hVel;
  1518.         thePixelShatter[i].v += thePixelShatter[i].vVel;    
  1519.         thePixelShatter[i].hVel += RandomInt(5) - 2;
  1520.         thePixelShatter[i].vVel += kGravity + RandomInt(5) - 2;
  1521.         
  1522.         if (    (thePixelShatter[i].type == kShatterLavaBubble) ||
  1523.                 (thePixelShatter[i].type == kShatterLavaSplash) ||
  1524.                 (thePixelShatter[i].type == kShatterLavaBurst))
  1525.         {
  1526.             thePixelShatter[i].color += RandomInt(3) - 1;
  1527.             if (thePixelShatter[i].color > 220)
  1528.             {
  1529.                 thePixelShatter[i].color = 220; 
  1530.             }
  1531.             else if (thePixelShatter[i].color < 216)
  1532.             {
  1533.                 thePixelShatter[i].color = 216;
  1534.             }
  1535.         }
  1536.         else if ((thePixelShatter[i].type == kShatterPlatformLightning))
  1537.         {
  1538.             if (thePixelShatter[i].duration > 18)
  1539.             {
  1540.                 thePixelShatter[i].color = 5;
  1541.             }
  1542.             else if (thePixelShatter[i].duration > 12)
  1543.             {
  1544.                 thePixelShatter[i].color = 47;
  1545.             }
  1546.             else if (thePixelShatter[i].duration > 6)
  1547.             {
  1548.                 thePixelShatter[i].color = 89;
  1549.             }
  1550.             else
  1551.             {
  1552.                 thePixelShatter[i].color = 255;
  1553.             }
  1554.         }
  1555.         else if (    (thePixelShatter[i].type == kShatterObeliskTip) ||
  1556.                      (thePixelShatter[i].type == kShatterLightningDust) ||
  1557.                      (thePixelShatter[i].type == kShatterPlayerDeath) ||
  1558.                      (thePixelShatter[i].type == kShatterEnemyDeath) ||
  1559.                      (thePixelShatter[i].type == kShatterPlayerEnemyScrape))
  1560.         {
  1561.             // white 0
  1562.             // yellow 1,2,3,4,5
  1563.             // orange 11, 17, 23, 29
  1564.             // red 35, ... 216, 218, 220, 222, 223
  1565.             // black 255
  1566.             
  1567.             unsigned char blackBodyTable[17] = {    0,                    // white
  1568.                                                 1,2,3,4,5,            // yellow (5)
  1569.                                                 11,17,23,29,        // orange (4)
  1570.                                                 35, 216, 218, 220, 222, 223, // red (6)
  1571.                                                 255                    // black
  1572.                                             };
  1573.                                             
  1574.             if (thePixelShatter[i].duration >= 17)
  1575.             {
  1576.                 thePixelShatter[i].color = 0;
  1577.             }
  1578.             else
  1579.             {
  1580.                 thePixelShatter[i].color = blackBodyTable[thePixelShatter[i].duration];
  1581.             }
  1582.         }
  1583.         else
  1584.         {
  1585.             if (thePixelShatter[i].duration > 23)
  1586.             {
  1587.                 thePixelShatter[i].color = 0;
  1588.             }
  1589.             else
  1590.             {
  1591.                 thePixelShatter[i].color = 255 - (thePixelShatter[i].duration / 2);
  1592.             }
  1593.         }
  1594.         
  1595.         thePixelShatter[i].h %= (640 * 16) ;
  1596.         
  1597.         if ((thePixelShatter[i].h < 0) || (thePixelShatter[i].h >= (640 * 16)) ||
  1598.             (thePixelShatter[i].v < 0) || (thePixelShatter[i].v >= (480 * 16)))
  1599.         {
  1600.             thePixelShatter[i] = thePixelShatter[numPixelShatter];
  1601.             numPixelShatter--;
  1602.             continue;
  1603.         }
  1604.         
  1605.         i++;
  1606.     }
  1607. }
  1608.  
  1609. void StartPixelShatterRect(Rect *r, short dH, short dV, short type)
  1610. {
  1611.     StartPixelShatter(r->left, r->top, dH, dV, type);
  1612.     StartPixelShatter(r->left, r->bottom, dH, dV, type);
  1613.     StartPixelShatter(r->right, r->top, dH, dV, type);
  1614.     StartPixelShatter(r->right, r->bottom, dH, dV, type);
  1615.     StartPixelShatter((r->left + r->right) / 2, (r->top + r->bottom) / 2, dH, dV, type);    
  1616. }
  1617.  
  1618. void StartPixelShatter(short h, short v, short hVel, short vVel, short type)
  1619.     signed short hOffset[9] = {0,16,16,0,-16,-16,-16,0,16};
  1620.     signed short vOffset[9] = {0,0,-16,-16,-16,0,16,16,16};    
  1621.     int itr;
  1622.     unsigned char color;
  1623.     
  1624.     h *= 16;
  1625.     v *= 16;
  1626.  
  1627.     if ((numPixelShatter + 10) >= kMaxPixelShatter)
  1628.     {
  1629.         return;
  1630.     }
  1631.     
  1632.     
  1633.     if (    (type == kShatterLavaBubble) ||
  1634.             (type == kShatterLavaSplash) ||
  1635.             (type == kShatterLavaBurst) ||
  1636.             (type == kShatterPlayerDeath) ||
  1637.             (type == kShatterEnemyDeath))
  1638.     {
  1639.         color = 215;
  1640.     }
  1641.     else if (type == kShatterPlatformLightning)
  1642.     {
  1643.         color = 5;
  1644.     }
  1645.     else
  1646.     {
  1647.         color = 0;
  1648.     }
  1649.  
  1650.  
  1651.     for(itr = 0; itr < 9; itr++)
  1652.     {
  1653.         thePixelShatter[numPixelShatter].h = h + hOffset[itr];
  1654.         thePixelShatter[numPixelShatter].v = v + vOffset[itr];
  1655.         thePixelShatter[numPixelShatter].h = h + hOffset[itr] + RandomInt(20) - 10;
  1656.         thePixelShatter[numPixelShatter].v = v + vOffset[itr] + RandomInt(20) - 10;
  1657.         thePixelShatter[numPixelShatter].hVel = hVel + hOffset[itr];
  1658.         thePixelShatter[numPixelShatter].vVel = vVel + vOffset[itr];
  1659.         thePixelShatter[numPixelShatter].color = color;
  1660.         
  1661.         if ((type == kShatterLavaSplash) || (type == kShatterPlatformLightning))
  1662.         {
  1663.             thePixelShatter[numPixelShatter].duration = 400;
  1664.         }
  1665.         else if ((type == kShatterPlatformScrape) || (type == kShatterPlatformHit))
  1666.         {
  1667.             thePixelShatter[numPixelShatter].duration = RandomInt(15) + 10;
  1668.         }
  1669.         else
  1670.         {
  1671.             thePixelShatter[numPixelShatter].duration = RandomInt(21) + 20;
  1672.         }
  1673.         thePixelShatter[numPixelShatter].type = type;
  1674.         
  1675.         numPixelShatter++;
  1676.     }
  1677. }
  1678.  
  1679. void HandleScoreFloaters(void)
  1680. {
  1681.     int i = 0;
  1682.     
  1683.     while(i < numScoreFloater)
  1684.     {
  1685.         if (theScoreFloater[i].duration == 0)
  1686.         {
  1687.             theScoreFloater[i] = theScoreFloater[numScoreFloater];
  1688.             numScoreFloater--;
  1689.             continue;
  1690.         }
  1691.         
  1692.         theScoreFloater[i].location.v -= 2;
  1693.         theScoreFloater[i].duration--;
  1694.         i++;
  1695.     }
  1696. }
  1697.  
  1698. void StartScoreFloater(unsigned long score, Point where)
  1699. {
  1700.     if (where.h > 419) { where.h = 419; }
  1701.     if (where.v > 459) { where.v = 549; }
  1702.     if (where.h < 0) {  where.h = 0;}
  1703.     if (where.v < 0) { where.v = 0; }
  1704.     
  1705.     theScoreFloater[numScoreFloater].score = score;
  1706.     theScoreFloater[numScoreFloater].location = where;
  1707.     theScoreFloater[numScoreFloater].duration = 20;
  1708.     numScoreFloater++;
  1709. }